﻿using api_identity.Data;
using api_identity.Data.Entities;
using api_identity.Models.Request;
using api_identity.Models.Response;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace api_identity.Controllers
{
    public class UsersController : BasicController
    {
        private readonly ILogger<UsersController> _logger;
        private readonly UserManager<UserEntity> _userManager;
        private readonly RoleManager<RoleEntity> _roleManager;
        private readonly ApplicationDbContext _context;

        public UsersController(ILogger<UsersController> logger, UserManager<UserEntity> userManager, RoleManager<RoleEntity> roleManager, ApplicationDbContext context)
        {
            this._logger = logger;
            this._userManager = userManager;
            this._roleManager = roleManager;
            this._context = context;
        }

        [HttpPost]
        public async Task<IActionResult> Add(ReqAddUser addUser)
        {
            var user = new UserEntity
            {
                UserName = addUser.UserName,
                Email = addUser.Email,
            };
            var result = await this._userManager.CreateAsync(user, addUser.Pwd);

            //var role = new RoleEntity { Name = "test" };
            //await this._roleManager.CreateAsync(role);

            //await this._userManager.AddClaimsAsync(user, new List<Claim>
            //{
            //    new Claim("rid",role.Id),
            //    new Claim("rid",role.Id.Substring(0,1))
            //});

            return base.Result(result.Succeeded, string.Join("; ", result.Errors.Select(e => $"{e.Code}:{e.Description}")));
        }

        [HttpPost("token")]
        public async Task<IActionResult> GetToken(ReqLoginUser loginUser, [FromServices] SignInManager<UserEntity> signInManager)
        {
            var user = await this._userManager.FindByEmailAsync(loginUser.Email);
            //await this._userManager.FindByNameAsync(loginUser.UserName);//用户名
            //await this._userManager.FindByLoginAsync("", "");//第三方，Google等
            //扩展其他方式：手机号等
            if (user == null)
            {
                return base.NotFoundResult();
            }
            var result = await signInManager.PasswordSignInAsync(user, loginUser.PassWord, false, true);
            if (!result.Succeeded)
            {
                //result.IsLockedOut 此用户被锁定
                //result.RequiresTwoFactor 需要二次验证
                //result.IsNotAllowed 
                return base.FailResult();
            }
            //string token = await this.HttpContext.GetTokenAsync("Google");
            var claims = await this._userManager.GetClaimsAsync(user);
            return base.OKResultOfT(new RepLoginedUserToken
            {
                AccessToken = claims.GetJwtAuthenticationToken(out DateTime expiresTime),
                ExpiresSeconds = Convert.ToInt32((expiresTime - DateTime.Now).TotalSeconds),
                RefreshToken = await GetRefreshTokenAsync(user, expiresTime)
            });
        }

        [HttpPost("refresh_token")]
        public async Task<IActionResult> RefreshToken([FromForm] string refreshToken)
        {
            long nowTimeStamp = DateTime.Now.GetMillisecondsTimeStamp();
            var entity = await this._context.RefreshTokens.Where(rt => rt.Key == refreshToken).FirstOrDefaultAsync();
            if (entity == null)
            {
                return base.NotFoundResult("not found token");
            }
            else if (nowTimeStamp > entity.ExpireTimeStamp)
            {
                return base.FailResult("refresh_token expired");
            }

            var unExpiredCount = await this._context.RefreshTokens.Where(rt => rt.UserId == entity.UserId).Where(rt => rt.ExpireTimeStamp > nowTimeStamp).CountAsync();

            if (unExpiredCount > 10)
            {
                return base.FailResult("try again later");
            }
            var user = await this._userManager.FindByIdAsync(entity.UserId);
            if (user == null)
            {
                return base.NotFoundResult("not found user");
            }
            var claims = await this._userManager.GetClaimsAsync(user);
            return base.OKResultOfT(new RepLoginedUserToken
            {
                AccessToken = claims.GetJwtAuthenticationToken(out DateTime expiresTime),
                ExpiresSeconds = Convert.ToInt32((expiresTime - DateTime.Now).TotalSeconds),
                RefreshToken = await GetRefreshTokenAsync(user, expiresTime)
            });
        }

        private async Task<string> GetRefreshTokenAsync(UserEntity user, DateTime expiresTime)
        {
            string refreshToken = Convert.ToBase64String(Encoding.ASCII.GetBytes(user.Id.Replace("-", "") + DateTime.Now.ToString("yyyyMMddHHmmss")));
            await this._context.RefreshTokens.AddAsync(new RefreshTokenEntity(user.Id, refreshToken, expiresTime.AddMinutes(5)));
            await this._context.SaveChangesAsync();
            return refreshToken;
        }

    }
}
